// City.cpp: implementation of the CCity class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "City.h"

#define CELLINGHEIGHT	1.0f
#define CELLINGTHICKNES	0.1f
#define CELLINGSIZE		0.2f - 0.01f

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CCity::CCity( PDIRECT3DDEVICE8 pDevice ) : CEfx( pDevice ), 
										   m_bsSystem( 25 ),
										   m_pvbGrid(NULL),
										   m_pvbThickGrid(NULL),
										   m_pvbFlarez(NULL),
										   m_pvbCelling(NULL),
										   m_pibCelling(NULL),
										   m_piqFade(NULL),
										   m_piqEdge(NULL),
										   m_piqEarth(NULL)
		
{
	try
	{
		m_sCity.Initialize( m_pDevice );
		m_sCity.Load( "data\\scenes\\city.scene" );

		LoadAlphaTexture( "cityedge.bmp" );
		LoadAlphaTexture( "cityedgemask.bmp" );
		LoadAlphaTexture( "partblob.bmp" );
		LoadAlphaTexture( "earth.bmp" );
	}
	catch( he3d_CException ex )
	{
		TCHAR				ptsErr[] = "unable to initialize/load scene, error code: 00";
		_stprintf( ptsErr, "unable to initialize/load scene, error code: %2i", ex.code );

		throw CSystemException( ptsErr );
	}
	catch( CTextureException )
	{
		throw CSystemException( "unable to load textures" );
	}

	if( FAILED( m_pDevice->CreateVertexBuffer( 400*sizeof(SIMPLEVERTEX), 0, FVF_SIMPLEVERTEX, D3DPOOL_DEFAULT, &m_pvbGrid ) ) )
		throw CSystemException( "unable to create grid vertex buffer" );

	if( FAILED( m_pDevice->CreateVertexBuffer( 22*6*sizeof(SIMPLEVERTEX), 0, FVF_SIMPLEVERTEX, D3DPOOL_DEFAULT, &m_pvbThickGrid ) ) )
		throw CSystemException( "unable to create thick grid vertex buffer" );

	if( FAILED( m_pDevice->CreateVertexBuffer( 25*25*25*sizeof(PARTICLEVERTEX), D3DUSAGE_POINTS, FVF_PARTICLEVERTEX, D3DPOOL_DEFAULT, &m_pvbFlarez ) ) )
		throw CSystemException( "unable to create flarez vertex buffer" );

	if( FAILED( m_pDevice->CreateVertexBuffer( 25*8*sizeof(SIMPLEVERTEX), 0, FVF_PARTICLEVERTEX, D3DPOOL_DEFAULT, &m_pvbCelling ) ) )
		throw CSystemException( "unable to create celling vertex buffer" );

	if( FAILED( m_pDevice->CreateIndexBuffer( 25*36*sizeof(WORD), 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &m_pibCelling ) ) )
		throw CSystemException( "unable to create celling index buffer" );
	
	m_piqFade = new CImageQuad( m_pDevice, 0, 0, 512, 512 );
	m_piqEdge = new CImageQuad( m_pDevice, 0, 0, 512, 512 );
	m_piqEarth = new CImageQuad( m_pDevice, 20, 20, 100, 128 );


	m_piqEdge->SetColor( 0x0 );
	m_piqEarth->SetColor( 0x0 );

	m_sCity.SetVieport( 0.1f, 100.0f, 0.75f );

	m_psmCity = (he3d_CSegmentedMesh*)m_sCity.GetObject( "MIASTO" );

	PSIMPLEVERTEX			pVertices;

	m_pvbGrid->Lock( 0, 0, (LPBYTE*)&pVertices, 0 );

	for( DWORD i = 0 ; i < 100 ; i++ )
	{
		*(pVertices++) = SIMPLEVERTEX( -0.5f, -0.06f, -0.5f + (FLOAT)i/99.0f, 0x808080 );
		*(pVertices++) = SIMPLEVERTEX(  0.5f, -0.06f, -0.5f + (FLOAT)i/99.0f, 0x808080 );
	}

	for( i = 0 ; i < 100 ; i++ )
	{
		*(pVertices++) = SIMPLEVERTEX( -0.5f + (FLOAT)i/99.0f, -0.06f,  0.5f, 0x808080 );
		*(pVertices++) = SIMPLEVERTEX( -0.5f + (FLOAT)i/99.0f, -0.06f, -0.5f, 0x808080 );
	}

	m_pvbGrid->Unlock();

	m_pvbThickGrid->Lock( 0, 0, (LPBYTE*)&pVertices, 0 );

	for( i = 0; i < 11 ; i++ )
	{
		*(pVertices++) = SIMPLEVERTEX( -0.5f, -0.06f, -0.5f + (FLOAT)i/11.0f - 0.0002f, 0x808080 );
		*(pVertices++) = SIMPLEVERTEX(  0.5f, -0.06f, -0.5f + (FLOAT)i/11.0f - 0.0002f, 0x808080 );
		*(pVertices++) = SIMPLEVERTEX( -0.5f, -0.06f, -0.5f + (FLOAT)i/11.0f + 0.0002f, 0x808080 );

		*(pVertices++) = SIMPLEVERTEX( -0.5f, -0.06f, -0.5f + (FLOAT)i/11.0f + 0.0002f, 0x808080 );
		*(pVertices++) = SIMPLEVERTEX(  0.5f, -0.06f, -0.5f + (FLOAT)i/11.0f - 0.0002f, 0x808080 );
		*(pVertices++) = SIMPLEVERTEX(  0.5f, -0.06f, -0.5f + (FLOAT)i/11.0f + 0.0002f, 0x808080 );
	}

	for( i = 0; i < 11 ; i++ )
	{
		*(pVertices++) = SIMPLEVERTEX( -0.5f + (FLOAT)i/11.0f - 0.0002f, -0.06f, -0.5f, 0x808080 );
		*(pVertices++) = SIMPLEVERTEX( -0.5f + (FLOAT)i/11.0f - 0.0002f, -0.06f,  0.5f, 0x808080 );
		*(pVertices++) = SIMPLEVERTEX( -0.5f + (FLOAT)i/11.0f + 0.0002f, -0.06f, -0.5f, 0x808080 );

		*(pVertices++) = SIMPLEVERTEX( -0.5f + (FLOAT)i/11.0f + 0.0002f, -0.06f, -0.5f, 0x808080 );
		*(pVertices++) = SIMPLEVERTEX( -0.5f + (FLOAT)i/11.0f - 0.0002f, -0.06f,  0.5f, 0x808080 );
		*(pVertices++) = SIMPLEVERTEX( -0.5f + (FLOAT)i/11.0f + 0.0002f, -0.06f,  0.5f, 0x808080 );
	}

	m_pvbThickGrid->Unlock();

	for( i = 0 ; i < 5 ; i++ )	
	{
		m_bsSystem.AddBlob( &m_b3dBlobs[i] );
		m_b3dBlobs[i].m_fDensity = 0.8f;
		m_b3dBlobs[i].m_fRadius = 5.0f;
	}

	m_vBlobs[0] = 5.0f*he3d_CVector( -1.0f,  0.5f, -1.0f );
	m_vBlobs[1] = 5.0f*he3d_CVector(  1.0f, -0.8f,  0.0f );
	m_vBlobs[2] = 5.0f*he3d_CVector(  0.3f, -0.5f,  0.2f );
	m_vBlobs[3] = 5.0f*he3d_CVector(  0.4f,  0.2f,  0.1f );
	m_vBlobs[4] = 5.0f*he3d_CVector( -0.6f,  0.1f, -0.2f );

	LPWORD					pIndices;

	m_pvbCelling->Lock( 0, 0, (LPBYTE*)&pVertices, 0 );

	for( i = 0 ; i < 5 ; i++ )
		for( DWORD j = 0 ; j < 5 ; j++ )
		{
			*(pVertices++) = SIMPLEVERTEX( (FLOAT)j/5.0f - 0.5f, CELLINGHEIGHT, (FLOAT)i/5.0f - 0.5f, 0x40000000 );
			*(pVertices++) = SIMPLEVERTEX( (FLOAT)j/5.0f + CELLINGSIZE - 0.5f, CELLINGHEIGHT, (FLOAT)i/5.0f - 0.5f, 0x40000000 );
			*(pVertices++) = SIMPLEVERTEX( (FLOAT)j/5.0f + CELLINGSIZE - 0.5f, CELLINGHEIGHT, (FLOAT)i/5.0f + CELLINGSIZE - 0.5f, 0x40000000 );
			*(pVertices++) = SIMPLEVERTEX( (FLOAT)j/5.0f - 0.5f, CELLINGHEIGHT, (FLOAT)i/5.0f + CELLINGSIZE - 0.5f, 0x40000000 );
			*(pVertices++) = SIMPLEVERTEX( (FLOAT)j/5.0f - 0.5f, CELLINGHEIGHT + CELLINGTHICKNES, (FLOAT)i/5.0f - 0.5f, 0x40000000 );
			*(pVertices++) = SIMPLEVERTEX( (FLOAT)j/5.0f + CELLINGSIZE - 0.5f, CELLINGHEIGHT + CELLINGTHICKNES, (FLOAT)i/5.0f - 0.5f, 0x40000000 );
			*(pVertices++) = SIMPLEVERTEX( (FLOAT)j/5.0f + CELLINGSIZE - 0.5f, CELLINGHEIGHT + CELLINGTHICKNES, (FLOAT)i/5.0f + CELLINGSIZE - 0.5f, 0x40000000 );
			*(pVertices++) = SIMPLEVERTEX( (FLOAT)j/5.0f - 0.5f, CELLINGHEIGHT + CELLINGTHICKNES, (FLOAT)i/5.0f + CELLINGSIZE - 0.5f, 0x40000000 );						
		}

	m_pvbCelling->Unlock();

	m_pibCelling->Lock( 0, 0, (LPBYTE*)&pIndices, 0 );

	for( i = 0 ; i < 25 ; i++ )
	{
		// up
		*(pIndices++) = (WORD)(8*i + 0 );
		*(pIndices++) = (WORD)(8*i + 1 );
		*(pIndices++) = (WORD)(8*i + 2 );

		*(pIndices++) = (WORD)(8*i + 0 );
		*(pIndices++) = (WORD)(8*i + 2 );
		*(pIndices++) = (WORD)(8*i + 3 );

		//down
		*(pIndices++) = (WORD)(8*i + 4 );
		*(pIndices++) = (WORD)(8*i + 5 );
		*(pIndices++) = (WORD)(8*i + 6 );

		*(pIndices++) = (WORD)(8*i + 4 );
		*(pIndices++) = (WORD)(8*i + 6 );
		*(pIndices++) = (WORD)(8*i + 7 );

		//left
		*(pIndices++) = (WORD)(8*i + 0 );
		*(pIndices++) = (WORD)(8*i + 3 );
		*(pIndices++) = (WORD)(8*i + 4 );

		*(pIndices++) = (WORD)(8*i + 4 );
		*(pIndices++) = (WORD)(8*i + 3 );
		*(pIndices++) = (WORD)(8*i + 7 );

		//right
		*(pIndices++) = (WORD)(8*i + 1 );
		*(pIndices++) = (WORD)(8*i + 2 );
		*(pIndices++) = (WORD)(8*i + 5 );

		*(pIndices++) = (WORD)(8*i + 5 );
		*(pIndices++) = (WORD)(8*i + 2 );
		*(pIndices++) = (WORD)(8*i + 6 );

		//front
		*(pIndices++) = (WORD)(8*i + 2 );
		*(pIndices++) = (WORD)(8*i + 3 );
		*(pIndices++) = (WORD)(8*i + 7 );

		*(pIndices++) = (WORD)(8*i + 7 );
		*(pIndices++) = (WORD)(8*i + 2 );
		*(pIndices++) = (WORD)(8*i + 6 );

		//back
		*(pIndices++) = (WORD)(8*i + 0 );
		*(pIndices++) = (WORD)(8*i + 1 );
		*(pIndices++) = (WORD)(8*i + 4 );

		*(pIndices++) = (WORD)(8*i + 4 );
		*(pIndices++) = (WORD)(8*i + 1 );
		*(pIndices++) = (WORD)(8*i + 5 );
	}
}

CCity::~CCity()
{
   SAFE_RELEASE( m_pvbGrid );
   SAFE_RELEASE( m_pvbThickGrid );
   SAFE_RELEASE( m_pvbFlarez );
   SAFE_RELEASE( m_pvbCelling );
   SAFE_RELEASE( m_pibCelling );

   SAFE_DELETE( m_piqFade );
   SAFE_DELETE( m_piqEdge );
   SAFE_DELETE( m_piqEarth );
}


BOOL CCity::UpdateFrame( FLOAT fTime )
{
	m_sCity.Transform( fTime*30.0f );

	D3DMATERIAL8			d3dmat;
	
	d3dmat.Ambient.r = 1.0f;
	d3dmat.Ambient.g = 1.0f;
	d3dmat.Ambient.b = 1.0f;
	d3dmat.Diffuse.r = 0.4f;
	d3dmat.Diffuse.g = 0.4f;
	d3dmat.Diffuse.b = 0.4f;
	d3dmat.Emissive.r = 0.1f;
	d3dmat.Emissive.g = 0.1f;
	d3dmat.Emissive.b = 0.1f;
	d3dmat.Specular.r = 1.0f;
	d3dmat.Specular.g = 1.0f;
	d3dmat.Specular.b = 1.0f;
	d3dmat.Power = 100.0f;

	m_pDevice->SetMaterial( &d3dmat );

	
	D3DLIGHT8				d3dlit;	
	he3d_CVector			v1 = he3d_CVector( -150.0f,  70.0f, -100.0f )*YRotationMtx( 0.7f*fTime );
	he3d_CVector			v2 = he3d_CVector(  100.0f, -50.0f,  100.0f )*YRotationMtx( 0.7f*fTime );
	
	ZeroMemory( &d3dlit, sizeof(D3DLIGHT8) );

	d3dlit.Type = D3DLIGHT_POINT;	
	d3dlit.Attenuation0 = 1.0f;
	d3dlit.Range = LIGHT_RANGE_MAX;

	d3dlit.Position.x = v1.x;
	d3dlit.Position.y = v1.y;
	d3dlit.Position.z = v1.z;

	d3dlit.Specular.r = 1.0f;
	d3dlit.Specular.g = 1.0f;
	d3dlit.Specular.b = 1.0f;
	d3dlit.Diffuse.r = 1.0f;
	d3dlit.Diffuse.g = 1.0f;
	d3dlit.Diffuse.b = 1.0f;

	m_pDevice->SetLight( 0, &d3dlit );

	d3dlit.Position.x = v2.x;
	d3dlit.Position.y = v2.y;
	d3dlit.Position.z = v2.z;

	m_pDevice->SetLight( 1, &d3dlit );		


	m_b3dBlobs[0].m_vPos = m_vBlobs[0]*RotationMtx(  0.7f*fTime, -0.5f*fTime,       fTime );
	m_b3dBlobs[1].m_vPos = m_vBlobs[1]*RotationMtx(  0.3f*fTime,  0.6f*fTime, -0.5f*fTime );
	m_b3dBlobs[2].m_vPos = m_vBlobs[2]*RotationMtx( -0.8f*fTime,  0.5f*fTime,  0.3f*fTime );
	m_b3dBlobs[3].m_vPos = m_vBlobs[3]*RotationMtx(       fTime, -0.8f*fTime,  0.5f*fTime );
	m_b3dBlobs[4].m_vPos = m_vBlobs[4]*RotationMtx(  0.5f*fTime,      -fTime,  0.8f*fTime );

	m_bsSystem.CalculateDensity();

	FLOAT*				pGrid = m_bsSystem.GetGrid();

	PPARTICLEVERTEX		pV;

	m_pvbFlarez->Lock( 0, 0, (LPBYTE*)&pV, 0 );

	DWORD				dwIndex = 0;

	for( DWORD i = 0 ; i < 25 ; i++ )
		for( DWORD j = 0 ; j < 25 ; j++ )
			for( DWORD k = 0 ; k < 25 ; k++, dwIndex++ )
			{
				pV[dwIndex].p = he3d_CVector( -0.5f + (FLOAT)k/24.0f, -0.5f + (FLOAT)j/24.0f, -0.5f + (FLOAT)i/24.0f );
				pV[dwIndex].size = 0.5f*pGrid[dwIndex];
				pV[dwIndex].color = 0xffffff;				
			}

	m_pvbFlarez->Unlock();

	m_mtxBlobs = ScaleMtx( 2.5f )*RotationMtx( fTime*0.2f, fTime*0.5f, fTime*0.4f );

	return TRUE;
}

BOOL CCity::RenderEfx()
{
	m_pDevice->Clear( 0, NULL, D3DCLEAR_ZBUFFER|D3DCLEAR_TARGET|D3DCLEAR_STENCIL, 0xffffff, 1.0f, 0 );	

	m_pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );	
	m_pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
	m_pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );

	m_pDevice->SetTexture( 0, GetTexture( "cityedgemask.bmp" ) );
	
	m_pDevice->SetRenderState( D3DRS_STENCILENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_STENCILREF, 0x1 );
	m_pDevice->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE );

	m_pDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_ALPHAREF, 0x80 );
	m_pDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );

	m_piqFade->Render( m_pDevice );

	m_pDevice->SetTexture( 0, NULL );
	m_pDevice->SetRenderState( D3DRS_STENCILENABLE, FALSE );	
	m_pDevice->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_KEEP );	
	m_pDevice->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );

	m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	m_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

	m_pDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );

	m_pDevice->SetTransform( D3DTS_WORLD, ScaleMtx( 10.0f, 1.0f, 10.0f ) );

	m_pDevice->SetVertexShader( FVF_SIMPLEVERTEX );

	m_pDevice->SetStreamSource( 0, m_pvbCelling, sizeof(SIMPLEVERTEX) );
	m_pDevice->SetIndices( m_pibCelling, 0 );
	
	m_pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 8*25, 0, 12*25 );

	m_pDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );

	m_pDevice->SetStreamSource( 0, m_pvbGrid, sizeof(SIMPLEVERTEX) );
	m_pDevice->DrawPrimitive( D3DPT_LINELIST, 0, 200 );

	m_pDevice->SetStreamSource( 0, m_pvbThickGrid, sizeof(SIMPLEVERTEX) );
	m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 44 );

	m_pDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
	m_pDevice->SetRenderState( D3DRS_SPECULARENABLE, TRUE );

	m_pDevice->SetVertexShader( FVF_VERTEX );
	m_pDevice->SetStreamSource( 0, m_psmCity->pvbVertices, sizeof(VERTEX) );
	m_pDevice->SetIndices( m_psmCity->pibIndices, 0 );

	m_pDevice->SetTransform( D3DTS_WORLD, m_psmCity->mtxTransform );

	m_pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, m_psmCity->dwVerticesCount, 0, m_psmCity->dwFacesCount );

	m_pDevice->SetTransform( D3DTS_PROJECTION, ProjectionMtx( 30.0f, 0.75f, 1.0f, 1000.0f ) );
	m_pDevice->SetTransform( D3DTS_VIEW, CameraMtx( he3d_CVector( 0, 0, -3 ), he3d_CVector( 0, 0, 3 ), 0 ) );

	m_pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
	m_pDevice->SetRenderState( D3DRS_SPECULARENABLE, FALSE );

	m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	m_pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

	m_pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE );
    m_pDevice->SetRenderState( D3DRS_POINTSCALEENABLE,  TRUE );    
    m_pDevice->SetRenderState( D3DRS_POINTSIZE_MIN, FLT2DWORD(3.00f) );
    m_pDevice->SetRenderState( D3DRS_POINTSCALE_A,  FLT2DWORD(1.00f) );
    m_pDevice->SetRenderState( D3DRS_POINTSCALE_B,  FLT2DWORD(0.00f) );
    m_pDevice->SetRenderState( D3DRS_POINTSCALE_C,  FLT2DWORD(2.00f) );

	m_pDevice->SetRenderState( D3DRS_STENCILENABLE, TRUE );
	m_pDevice->SetRenderState( D3DRS_STENCILREF, 0x1 );
	m_pDevice->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_EQUAL );	

	m_pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE  );

	m_pDevice->SetTexture( 0, GetTexture( "partblob.bmp" ) );	

	m_pDevice->SetTransform( D3DTS_WORLD, m_mtxBlobs );

	m_pDevice->SetVertexShader( FVF_PARTICLEVERTEX );
	m_pDevice->SetStreamSource( 0, m_pvbFlarez, sizeof(PARTICLEVERTEX) );
	m_pDevice->DrawPrimitive( D3DPT_POINTLIST, 0, 25*25*25 );

	m_pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE );
    m_pDevice->SetRenderState( D3DRS_POINTSCALEENABLE,  FALSE ); 

	m_pDevice->SetRenderState( D3DRS_STENCILENABLE, FALSE );
	m_pDevice->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_ALWAYS );

	m_pDevice->SetTexture( 0, GetTexture( "cityedge.bmp" ) );
	m_piqEdge->Render( m_pDevice );

	m_pDevice->SetTexture( 0, GetTexture( "earth.bmp" ) );
	m_piqEarth->Render( m_pDevice );

	m_pDevice->SetTexture( 0, NULL );	

	m_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );

	return TRUE;
}